/************************************
*  Copyright (C) 2010 Julien MOTTIN.
*  Contact: julien.mottin@gueydan.eu
*
*  This file is part of Gueydan.
*
*  Gueydan is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  Gueydan is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with Gueydan.  If not, see <http://www.gnu.org/licenses/>.
*
*************************************/
// local
#include "repository.h"
#include "hop.h"
#include "fermentable.h"
#include "misc.h"
#include "yeast.h"

// QtCore
#include <QFileInfo>
#include <QDir>
#include <QFile>
#include <QTextStream>

// QtXml
#include <QDomDocument>

// String constants
static const QString s_HopDirName("Hops");
static const QString s_FermDirName("Fermentables");
static const QString s_MiscDirName("Miscs");
static const QString s_YeastDirName("Yeasts");

Repository::Repository(const QString &iDir)
:_Directory(iDir)
,_Status(Closed)
{
    _Hops._Map.clear();
    _Hops._HasChanged = false;
    _Ferms._Map.clear();
    _Ferms._HasChanged = false;
    _Miscs._Map.clear();
    _Miscs._HasChanged = false;
    _Yeasts._Map.clear();
    _Yeasts._HasChanged = false;
}

Repository::~Repository()
{
    Close();
}

void Repository::Close()
{
    if (_Status!=Closed)
    {
        // Commit if necessary
        if (_Status==ReadWrite)
        {
            CommitSet(s_HopDirName,&_Hops);
            CommitSet(s_FermDirName,&_Ferms);
            CommitSet(s_MiscDirName,&_Miscs);
            CommitSet(s_YeastDirName,&_Yeasts);
        }

        // Update status
        _Status = Closed;
    }
}

void Repository::OpenRead()
{
    _Status = ReadOnly;
    OpenSet(s_HopDirName,&_Hops);
    OpenSet(s_FermDirName,&_Ferms);
    OpenSet(s_MiscDirName,&_Miscs);
    OpenSet(s_YeastDirName,&_Yeasts);
}

void Repository::OpenWrite()
{
    _Status = ReadWrite;
    OpenSet(s_HopDirName,&_Hops);
    OpenSet(s_FermDirName,&_Ferms);
    OpenSet(s_MiscDirName,&_Miscs);
    OpenSet(s_YeastDirName,&_Yeasts);
}

int Repository::AddHop(Hop *ipHop)
{
    return AddObject(ipHop,&_Hops);
}

Hop * Repository::SearchHop(const QString &iHopName) const
{
    Hop *opHop = NULL;

    // Grab handler on hop if exists
    BeerXMLHandler hHop = SearchObject(iHopName,&_Hops);

    // Cast to Hop*
    opHop = (Hop*)hHop;
    if (opHop)
        opHop->AddRef();

    return opHop;
}

QStringList Repository::ListHopNames() const
{
    return ListObjNames(&_Hops);
}

int Repository::AddFerm(Fermentable *ipFerm)
{
    return AddObject(ipFerm,&_Ferms);
}

Fermentable * Repository::SearchFerm(const QString &iFermName) const
{
    Fermentable *opFerm = NULL;

    // Grab handler on ferm if exists
    BeerXMLHandler hFerm = SearchObject(iFermName,&_Ferms);

    // Cast to Fermentable*
    opFerm = (Fermentable*)hFerm;
    if (opFerm)
        opFerm->AddRef();

    return opFerm;
}

QStringList Repository::ListFermNames() const
{
    return ListObjNames(&_Ferms);
}

int Repository::AddMisc(Misc *ipMisc)
{
    return AddObject(ipMisc,&_Miscs);
}

Misc * Repository::SearchMisc(const QString &iMiscName) const
{
    Misc *opMisc = NULL;

    // Grab handler on misc if exists
    BeerXMLHandler hMisc = SearchObject(iMiscName,&_Miscs);

    // Cast to Misc*
    opMisc = (Misc*)hMisc;
    if (opMisc)
        opMisc->AddRef();

    return opMisc;
}

QStringList Repository::ListMiscNames() const
{
    return ListObjNames(&_Miscs);
}

int Repository::AddYeast(Yeast *ipYeast)
{
    return AddObject(ipYeast,&_Yeasts);
}

Yeast * Repository::SearchYeast(const QString &iYeastName) const
{
    Yeast *opYeast = NULL;

    // Grab handler on misc if exists
    BeerXMLHandler hYeast = SearchObject(iYeastName,&_Yeasts);

    // Cast to Yeast*
    opYeast = (Yeast*)hYeast;
    if (opYeast)
        opYeast->AddRef();

    return opYeast;
}

QStringList Repository::ListYeastNames() const
{
    return ListObjNames(&_Yeasts);
}

int Repository::AddObject(BeerXMLObject *ipObject, BeerXMLSet *ipSet)
{
    if (!ipObject || !ipSet) return -1;
    
    int oRC = 1;
    if (_Status==ReadWrite)
    {
        oRC = 2;
        
        BeerXMLHandler hObj(ipObject);
        QMap<QString,BeerXMLHandler>::iterator iter = ipSet->_Map.insert(ipObject->_Name,hObj);
        if (iter.value()==hObj)
        {
            oRC = 0;
            ipSet->_HasChanged = true;
        }
    }
    
    return oRC;
}

const BeerXMLHandler Repository::SearchObject(const QString &iObjName,const BeerXMLSet *ipSet) const
{
    // Check consistency & perform search
    BeerXMLHandler ohObj;
    if (ipSet && !iObjName.isEmpty())
        ohObj = ipSet->_Map.value(iObjName);

    return ohObj;
}

QStringList Repository::ListObjNames(const BeerXMLSet *ipSet) const
{
    if (ipSet)
        return ipSet->_Map.keys();

    return QStringList();
}

void Repository::OpenSet(const QString &iDirName, BeerXMLSet *ipSet)
{
    QDir RepoDir(_Directory);
    QFileInfo ObjInfo(RepoDir,iDirName);
    if (ObjInfo.isDir())
    {
        QDir ObjDir(ObjInfo.filePath());
        QStringList ObjList = ObjDir.entryList(QDir::Files);

        // Loop over Hop files
        for (int i=0;i<ObjList.size();i++)
        {
            // Build full obj path
            QString ObjFilePath = ObjDir.filePath(ObjList[i]);

            // Parse
            BeerXMLHandler hObj;
            if (0==hObj.CreateFromFile(ObjFilePath))
            {
                // add to map
                ipSet->_Map.insert(hObj->_Name,hObj);
            }
        }
    }
}

void Repository::CommitSet(const QString &iDirName, BeerXMLSet *ipSet)
{
    if (ipSet && ipSet->_HasChanged)
    {
        // Create dir if necessary
        QDir CurDir(_Directory);
        if (!(CurDir.cd(iDirName)))
            CurDir.mkdir(iDirName);

        // Go into hop Dir
        if (CurDir.cd(iDirName))
        {
            QMap<QString,BeerXMLHandler>::const_iterator iter = ipSet->_Map.constBegin();
            while (iter!=ipSet->_Map.constEnd())
            {
                BeerXMLHandler hObj = iter.value();
                QString FilePath = CurDir.filePath(hObj->_Name + ".xml");
                hObj.SaveToFile(FilePath);
                iter++;
            }
        }        
    }
}
